package com.capgemini.orf;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.net.URL;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.activation.DataHandler;
import javax.activation.DataSource;
import javax.mail.Message;
import javax.mail.MessagingException;
import javax.mail.Multipart;
import javax.mail.Part;
import javax.mail.Session;
import javax.mail.Transport;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeBodyPart;
import javax.mail.internet.MimeMessage;
import javax.mail.internet.MimeMultipart;
import javax.mail.util.ByteArrayDataSource;
import javax.ws.rs.Consumes;
import javax.ws.rs.FormParam;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.ResponseBuilder;
import javax.ws.rs.core.StreamingOutput;

import jxl.Workbook;
import jxl.write.Label;
import jxl.write.WritableSheet;
import jxl.write.WritableWorkbook;

import org.apache.poi.hssf.usermodel.HSSFCell;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;

import com.capgemini.orf.datamodel.Customer;
import com.capgemini.orf.datamodel.Employee;
import com.capgemini.orf.datamodel.Project;
import com.capgemini.orf.security.TokenManager;
import com.google.gdata.client.GoogleAuthTokenFactory.UserToken;
import com.google.gdata.client.docs.DocsService;
import com.google.gdata.client.spreadsheet.SpreadsheetService;
import com.google.gdata.data.MediaContent;
import com.google.gdata.data.media.MediaSource;
import com.google.gdata.data.spreadsheet.SpreadsheetEntry;
import com.google.gdata.data.spreadsheet.SpreadsheetFeed;
import com.google.gdata.util.AuthenticationException;

@Path("/")
public class OrfServlet {
	private static final Logger log = Logger.getLogger(OrfServlet.class
			.getName());

	public static final String ACCESS_CONTROL_ALLOW_ORIGIN_HEADER = "Access-Control-Allow-Origin";
	private static final String ACCESS_CONTROL_ALLOW_ORIGIN = "*";

	private static final String META_FEED_URL = "https://spreadsheets.google.com/feeds/spreadsheets/private/full";
	private static String spreadSheetName = "ORF_APPSTWO_v2";
	private static String yourApplicationName = "orf";

	// POI test
	@GET
	@Path("orf/excel_poi")
	@Produces("application/vnd.ms-excel")
	public StreamingOutput createExcelPoi2() throws Exception {
		return new StreamingOutput() {
			public void write(OutputStream output) throws IOException,
					WebApplicationException {
				HSSFWorkbook wb = new HSSFWorkbook();
				wb.createSheet("Demo POI");
				HSSFCell cell = wb.getSheet("Demo POI").createRow(0)
						.createCell(0);
				cell.setCellValue("POI say hello!");

				try {
					wb.write(output);
				} catch (IOException e) {
					e.printStackTrace();
					log.log(Level.SEVERE, "IO exception occurred...\nReason: "
							+ e.getMessage());
				}
			}
		};
	}

	// JExcel test
	@GET
	@Path("orf/excel")
	@Produces("application/vnd.ms-excel")
	public StreamingOutput createExcel2() throws Exception {
		return new StreamingOutput() {
			public void write(OutputStream output) throws IOException,
					WebApplicationException {
				try {
					WritableWorkbook w = Workbook.createWorkbook(output);
					WritableSheet s = w.createSheet("Demo JExcel", 0);

					s.addCell(new Label(0, 0, "JExcel says hello!"));
					w.write();
					w.close();
				} catch (Exception e) {
					e.printStackTrace();
					log.log(Level.SEVERE, "IO exception occurred...\nReason: "
							+ e.getMessage());
				}
			}
		};
	}

	// Create excel and send e-mail - google apis
	@POST
	@Path("orf/mail")
	@Consumes(MediaType.APPLICATION_FORM_URLENCODED)
	@Produces(MediaType.TEXT_PLAIN)
	public Response createExcelAndMail(
			@FormParam("fromEmail") String fromEmail,
			@FormParam("pwd") String pwd, @FormParam("toEmail") String toEmail)
			throws AuthenticationException, MessagingException, IOException {
		DocsService documentService = createDocumentService(fromEmail, pwd);

		SpreadsheetService spreadSheetService = createSpreadSheetService(
				fromEmail, pwd);

		SpreadsheetEntry spreadsheetEntry = loadSpreadSheet(spreadSheetService,
				spreadSheetName);
		byte[] spreadSheetData = getSpreadSheetData(spreadSheetService,
				documentService, spreadsheetEntry);
		sendMailWithAttachment(spreadSheetData, fromEmail, toEmail);

		String result = "Email has been sent from " + fromEmail + ".";
		ResponseBuilder builder = Response
				.status(200)
				.entity(result)
				.header(ACCESS_CONTROL_ALLOW_ORIGIN_HEADER,
						ACCESS_CONTROL_ALLOW_ORIGIN);

		return builder.build();
	}

	private static DocsService createDocumentService(String userEmail,
			String password) throws AuthenticationException {
		DocsService docsService = new DocsService(yourApplicationName);
		docsService.setUserCredentials(userEmail, password);
		return docsService;
	}

	private static SpreadsheetService createSpreadSheetService(
			String userEmail, String password) throws AuthenticationException {
		SpreadsheetService spreadSheetService = new SpreadsheetService(
				yourApplicationName);
		spreadSheetService.setUserCredentials(userEmail, password);
		return spreadSheetService;
	}

	private static SpreadsheetEntry loadSpreadSheet(
			SpreadsheetService spreadSheetService, String sheetName) {
		SpreadsheetEntry spreadSheetEntry = null;
		try {
			URL metafeedUrl = new URL(META_FEED_URL);
			SpreadsheetFeed feed = spreadSheetService.getFeed(metafeedUrl,
					SpreadsheetFeed.class);

			List<SpreadsheetEntry> spreadsheets = feed.getEntries();
			for (SpreadsheetEntry entry : spreadsheets) {
				if (entry.getTitle().getPlainText().equals(sheetName)) {
					spreadSheetEntry = entry;
					break;
				}
			}
		} catch (Exception e) {
			log.info("Loading Worksheet failed:" + e.getMessage());
		}

		return spreadSheetEntry;
	}

	private static byte[] getSpreadSheetData(
			SpreadsheetService spreadSheetService, DocsService documentService,
			SpreadsheetEntry spreadsheetEntry) throws IOException {
		String spreadSheetkey = spreadsheetEntry.getKey();
		byte[] data = null;
		InputStream inStream = null;
		ByteArrayOutputStream outStream = null;

		// Substitute the spreadsheets token for the docs token
		UserToken docsToken = (UserToken) documentService.getAuthTokenFactory()
				.getAuthToken();
		UserToken spreadsheetsToken = (UserToken) spreadSheetService
				.getAuthTokenFactory().getAuthToken();
		documentService.setUserToken(spreadsheetsToken.getValue());

		String exportUrl = "https://spreadsheets.google.com/feeds/download/spreadsheets"
				+ "/Export?key=" + spreadSheetkey + "&fmcmd=" + "xls";

		log.info("Exporting document from " + exportUrl);

		MediaContent mc = new MediaContent();
		mc.setUri(exportUrl);

		try {
			MediaSource ms = documentService.getMedia(mc);

			inStream = ms.getInputStream();
			outStream = new ByteArrayOutputStream();

			int c = 0;
			while ((c = inStream.read()) != -1) {
				outStream.write(c);
			}

			// Restore docs token for our DocList client
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			documentService.setUserToken(docsToken.getValue());

			if (inStream != null) {
				inStream.close();
			}

			if (outStream != null) {
				data = outStream.toByteArray();
				outStream.close();
			}
		}

		return data;
	}

	private static void sendMailWithAttachment(byte[] spreadSheetData,
			String userEmail, String recepientEmail)
			throws UnsupportedEncodingException, MessagingException {
		Properties props = new Properties();
		Session session = Session.getDefaultInstance(props, null);

		Message message = new MimeMessage(session);
		message.setFrom(new InternetAddress(userEmail, "Administrator"));

		message.addRecipient(Message.RecipientType.TO, new InternetAddress(
				recepientEmail, recepientEmail));

		String textBody = "Your Excel Sheet as attachment";

		Multipart mp = new MimeMultipart();

		MimeBodyPart textPart = new MimeBodyPart();
		textPart.setContent(textBody, "text/html");
		mp.addBodyPart(textPart);

		MimeBodyPart attachment = new MimeBodyPart();
		String excelFileName = spreadSheetName + ".xls";
		String filename = URLEncoder.encode(excelFileName, "UTF-8");
		attachment.setFileName(filename);
		attachment.setDisposition(Part.ATTACHMENT);

		DataSource src = new ByteArrayDataSource(spreadSheetData,
				"application/x-ms-excel");
		DataHandler handler = new DataHandler(src);
		attachment.setDataHandler(handler);
		mp.addBodyPart(attachment);

		message.setContent(mp);
		message.setSubject(String.format("Google Spreadsheet as attachment"));

		Transport.send(message);

		log.info("Mail with attachment file: " + excelFileName
				+ " sent successfully.");
	}

	// Services
	@POST
	@Path("orf/login")
	@Consumes(MediaType.APPLICATION_FORM_URLENCODED)
	@Produces(MediaType.TEXT_PLAIN)
	public String login(@FormParam("email") String email,
			@FormParam("password") String hashedPassword) throws Exception {
		// if hashedPasswords are equal
		return TokenManager.createTokenFor(email, hashedPassword);
		// else redirect to login page
	}

	@POST
	@Path("orf/validateToken")
	@Consumes(MediaType.APPLICATION_FORM_URLENCODED)
	@Produces(MediaType.TEXT_PLAIN)
	public String validateToken(@FormParam("token") String token)
			throws Exception {
		return (TokenManager.validateToken(token) ? "True" : "False");
	}

	@POST
	@Path("orf/searchCustomer")
	@Consumes(MediaType.APPLICATION_FORM_URLENCODED)
	@Produces(MediaType.APPLICATION_JSON)
	public List<Customer> searchCustomerByCriteria(
			@FormParam("searchCriteria") Customer searchCustomerDetails,
			@FormParam("sessionId") String sessionId) {
		return new ArrayList<Customer>();
	}

	@POST
	@Path("orf/searchProject")
	@Consumes(MediaType.APPLICATION_FORM_URLENCODED)
	@Produces(MediaType.APPLICATION_JSON)
	public List<Project> searchProjectByCriteria(
			@FormParam("searchCriteria") Project searchProjectDetails,
			@FormParam("sessionId") String sessionId) {
		return new ArrayList<Project>();
	}

	@POST
	@Path("orf/searchEmployee")
	@Consumes(MediaType.APPLICATION_FORM_URLENCODED)
	@Produces(MediaType.APPLICATION_JSON)
	public List<Employee> searchEmployeeByCriteria(
			@FormParam("searchCriteria") Employee searchProjectDetails,
			@FormParam("sessionId") String sessionId) {
		return new ArrayList<Employee>();
	}

	@GET
	@Path("orf/getCustomer/{id}/{sessionId}")
	@Produces(MediaType.APPLICATION_JSON)
	public Customer getCustomer(@PathParam("id") int id,
			@PathParam("sessionId") String sessionId) {
		return new Customer();
	}

	@GET
	@Path("orf/getProject/{id}/{sessionId}")
	@Produces(MediaType.APPLICATION_JSON)
	public Project getProject(@PathParam("id") int id,
			@PathParam("sessionId") String sessionId) {
		return new Project();
	}

	@GET
	@Path("orf/getEmployee/{id}/{sessionId}")
	@Produces(MediaType.APPLICATION_JSON)
	public Employee getEmployee(@PathParam("id") int id,
			@PathParam("sessionId") String sessionId) {
		return new Employee();
	}

	@POST
	@Path("orf/sendORF")
	@Consumes(MediaType.APPLICATION_FORM_URLENCODED)
	public Response sendOrf(@FormParam("customer") Customer customer,
			@FormParam("email") String email,
			@FormParam("comments") String comments,
			@FormParam("sessionId") String sessionId) {
		String result = "Email has been sent!";
		ResponseBuilder builder = Response
				.status(200)
				.entity(result)
				.header(ACCESS_CONTROL_ALLOW_ORIGIN_HEADER,
						ACCESS_CONTROL_ALLOW_ORIGIN);

		return builder.build();
	}
}